﻿using System;
using System.Collections.Generic;
using GameInstrument;
using GameInterface;
using GameLayer;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using GameBase;
using MainGame;
using Microsoft.Xna.Framework.Audio;

namespace GameCharacter
{
    public abstract class Character : Button, ICollision<Character>,
                                                            ICollision<Sprite>, ICollision<TiledLayer>
    {
        public const int MAX_BLOOD = 1000;
        protected const int COEFFICIENT_SPEED = 4;
        private int _level = 1;
        public int Level
        {
            get { return _level; }
            set { _level = value; }
        }
        private int _power = 4;
        public int Power
        {
            get { return _power; }
            set { _power = value; }
        }
        private int _blood = MAX_BLOOD;

        public int Blood
        {
            get { return _blood; }
            set { _blood = value; }
        }
        private List<Point> _targets = new List<Point>();
        public List<Point> Targets
        {
            get { return _targets; }
            set { _targets = value; }
        }
        public ContentManager Content { get; set; }
        public Vector2 Velocity { get; set; }
        private Rectangle _collisionDefine = Rectangle.Empty;
        protected bool completePreparePowerRelease = false;
        public Rectangle CollisionDefine
        {
            get
            {
                return _collisionDefine;
            }
            set
            {
                _collisionDefine = value;
            }
        }
        public Vector2 AttackTarget { get; set; }

        public SoundEffectInstance SoundAttack;
        public SoundEffectInstance SoundDie;
        public SoundEffectInstance SoundBeAttacked;
        public Character(ContentManager content)
        {
            this.Content = content;
        }

        #region constant
        public const int DOWN_HIT = 0;
        public const int DOWNLEFT_HIT = 1;
        public const int DOWNRIGHT_HIT = 2;
        public const int LEFT_HIT = 3;
        public const int RIGHT_HIT = 4;
        public const int UP_HIT = 5;
        public const int UPLEFT_HIT = 6;
        public const int UPRIGHT_HIT = 7;

        public const int GO_DOWN = 8;
        public const int GO_DOWNLEFT = 9;
        public const int GO_DOWNRIGHT = 10;

        public const int GO_LEFT = 11;
        public const int GO_RIGHT = 12;

        public const int GO_UP = 13;
        public const int GO_UPLEFT = 14;
        public const int GO_UPRIGHT = 15;

        public const int DIE_FROMDOWN = 16;
        public const int DIE_FROMDOWNLEFT = 17;
        public const int DIE_FROMDOWNRIGHT = 18;

        public const int DIE_FROMLEFT = 19;
        public const int DIE_FROMRIGHT = 20;

        public const int DIE_FROMUP = 21;
        public const int DIE_FROMUPLEFT = 22;
        public const int DIE_FROMUPRIGHT = 23;
        #endregion
        #region va chạm
        public bool CollisionWith(Character other, bool pixelLevel)
        {
            Sprite mySprite = this.Items[this._index];
            mySprite.Position = this.Position;
            mySprite.CollisionDefine = this.CollisionDefine;
            Sprite yourSprite = other.Items[other._index];
            yourSprite.Position = other.Position;
            yourSprite.CollisionDefine = other.CollisionDefine;
            return mySprite.CollisionWith(yourSprite, pixelLevel);
        }
        public bool CollisionWith(TiledLayer other, bool pixelLevel)
        {
            Sprite mySprite = this.Items[this._index];
            mySprite.Position = this.Position;
            mySprite.CollisionDefine = this.CollisionDefine;
            return other.CollisionWith(mySprite, pixelLevel);
        }
        public bool CollisionWith(Sprite other, bool pixelLevel)
        {
            Sprite mySprite = this.Items[this._index];
            mySprite.Position = this.Position;
            mySprite.CollisionDefine = this.CollisionDefine;
            return mySprite.CollisionWith(other, pixelLevel);
        }

        #endregion
        public virtual void GoToTarget(Vector2? target)
        {
            if (IsDying())
                return;
            if (target.HasValue)
            {
                this.Velocity = Vector2.Normalize(target.Value - Position) * Speed();
            }
            UpdateToDirect(DirectToIndexDirect(Velocity));
        }
        public virtual void GoUp()
        {
            UpdateToDirect(GO_UP);
        }
        public virtual void GoUpRight()
        {
            UpdateToDirect(GO_UPRIGHT);
        }
        public virtual void GoRight()
        {
            UpdateToDirect(GO_RIGHT);
        }
        public virtual void GoDownRight()
        {
            UpdateToDirect(GO_DOWNRIGHT);
        }
        public virtual void GoDown()
        {
            UpdateToDirect(GO_DOWN);
        }
        public virtual void GoDownLeft()
        {
            UpdateToDirect(GO_DOWNLEFT);
        }
        public virtual void GoLeft()
        {
            UpdateToDirect(GO_LEFT);
        }
        public virtual void GoUpLeft()
        {
            UpdateToDirect(GO_UPLEFT);
        }

        public virtual void BeginDie()
        {
            int current = this.Index;
            switch (this.Index)
            {
                case GO_DOWN:
                case DOWN_HIT:
                    this.Index = DIE_FROMDOWN;
                    break;

                case GO_DOWNLEFT:
                case DOWNLEFT_HIT:
                    this.Index = DIE_FROMDOWNLEFT;
                    break;

                case GO_DOWNRIGHT:
                case DOWNRIGHT_HIT:
                    this.Index = DIE_FROMDOWNRIGHT;
                    break;

                case GO_LEFT:
                case LEFT_HIT:
                    this.Index = DIE_FROMLEFT;
                    break;

                case GO_RIGHT:
                case RIGHT_HIT:
                    this.Index = DIE_FROMRIGHT;
                    break;

                case GO_UP:
                case UP_HIT:
                    this.Index = DIE_FROMUP;
                    break;

                case GO_UPLEFT:
                case UPLEFT_HIT:
                    this.Index = DIE_FROMUPLEFT;
                    break;

                case GO_UPRIGHT:
                case UPRIGHT_HIT:
                    this.Index = DIE_FROMUPRIGHT;
                    break;
            }

            if (current != this.Index && SoundDie != null)
                SoundDie.Play();
        }
        public virtual bool IsAttacking()
        {
            return this.Index > -1 && this._index < 8;
        }
        public virtual bool IsDying()
        {
            return this.Index > 15 && this._index < 24;
        }
        public virtual void Go()
        {
            if (IsDying())
                return;
            switch (this.Index)
            {
                case DOWN_HIT:
                    this.Index = GO_DOWN;
                    break;

                case DOWNLEFT_HIT:
                    this.Index = GO_DOWNLEFT;
                    break;

                case DOWNRIGHT_HIT:
                    this.Index = GO_DOWNRIGHT;
                    break;

                case LEFT_HIT:
                    this.Index = GO_LEFT;
                    break;

                case RIGHT_HIT:
                    this.Index = GO_RIGHT;
                    break;

                case UP_HIT:
                    this.Index = GO_UP;
                    break;

                case UPLEFT_HIT:
                    this.Index = GO_UPLEFT;
                    break;

                case UPRIGHT_HIT:
                    this.Index = GO_UPRIGHT;
                    break;
            }
        }
        public virtual bool Died()
        {
            return IsDying() && Current.Index == Current.nTextures - 1;
        }
        protected virtual void Die()
        {
            this.Visible = this.Enable = false;
        }

        public virtual void Attack()
        {
            if (IsDying())
                return;
            switch (this.Index)
            {
                case GO_DOWN:
                    this.Index = DOWN_HIT;
                    break;

                case GO_DOWNLEFT:
                    this.Index = DOWNLEFT_HIT;
                    break;

                case GO_DOWNRIGHT:
                    this.Index = DOWNRIGHT_HIT;
                    break;

                case GO_LEFT:
                    this.Index = LEFT_HIT;
                    break;

                case GO_RIGHT:
                    this.Index = RIGHT_HIT;
                    break;

                case GO_UP:
                    this.Index = UP_HIT;
                    break;

                case GO_UPLEFT:
                    this.Index = UPLEFT_HIT;
                    break;

                case GO_UPRIGHT:
                    this.Index = UPRIGHT_HIT;
                    break;
            }
        }

        public virtual void BeAttacked(int power)
        {
            this.Blood = Math.Max(0, Blood - power);
            if (SoundBeAttacked != null)
            {
                SoundBeAttacked.Play();
            }
        }
        protected override void UpdateEntitys(GameTime gameTime, object extraData)
        {
            base.UpdateEntitys(gameTime, extraData);
            Current.Update(gameTime);
            bool dying = IsDying();
            bool attacking = IsAttacking();
            if (dying || attacking)
            {
                Current.NextFrame();
            }
            if (dying)
            {
                if (Current.Index == Current.nTextures - 1)
                {
                    Visible = false;
                }
            }
            if (IsAttackCompleted())
            {
                completePreparePowerRelease = true;
                Go();
            }
        }

        public bool UpdateToDirect(int direct)
        {
            if (IsDying() || IsAttacking())
                return false;
            if (this.Index != direct)
            {
                this.Index = direct;
                this.Items[this.Index].Index = 0;
                return true;
            }
            return false;
        }

        protected Vector2 GetDirect()
        {
            Vector2 vdirect = Vector2.Zero;
            switch (this.Index)
            {
                case GO_DOWN:
                case DOWN_HIT:
                    vdirect = new Vector2(0, 1);
                    break;

                case GO_DOWNLEFT:
                case DOWNLEFT_HIT:
                    vdirect = new Vector2(-1, 1);
                    break;

                case GO_DOWNRIGHT:
                case DOWNRIGHT_HIT:
                    vdirect = new Vector2(1, 1);
                    break;

                case GO_LEFT:
                case LEFT_HIT:
                    vdirect = new Vector2(-1, 0);
                    break;

                case GO_RIGHT:
                case RIGHT_HIT:
                    vdirect = new Vector2(1, 0);
                    break;

                case GO_UP:
                case UP_HIT:
                    vdirect = new Vector2(0, -1);
                    break;

                case GO_UPLEFT:
                case UPLEFT_HIT:
                    vdirect = new Vector2(-1, -1);
                    break;

                case GO_UPRIGHT:
                case UPRIGHT_HIT:
                    vdirect = new Vector2(1, -1);
                    break;
            }
            return vdirect;
        }

        public int DirectToIndexDirect(Vector2 direct)
        {
            direct.Normalize();
            float absSin = Math.Abs(direct.Y);
            if (absSin >= Helper.SIN_67_5)
            {
                if (direct.Y < 0)
                {
                    return GO_UP;
                }
                return GO_DOWN;
            }
            else if (absSin < Helper.SIN_67_5 && absSin >= Helper.SIN_22_5)
            {
                if (direct.X > 0)
                {
                    if (direct.Y < 0)
                        return GO_UPRIGHT;
                    return GO_DOWNRIGHT;
                }
                else
                {
                    if (direct.Y < 0)
                    {
                        return GO_UPLEFT;
                    }
                    return GO_DOWNLEFT;
                }
            }
            else
            {
                if (direct.X > 0)
                    return GO_RIGHT;
                return GO_LEFT;
            }

        }

        public bool IsAttackCompleted()
        {
            return IsAttacking() && this.Current.Index == this.Current.nTextures - 1;
        }

        public virtual List<Weapon> ReleasePowerAttack()
        {
            return null;
        }

        public int ReleasePower()
        {
            return Power + this.Power + 10 + 5 * Level;
        }

        public int Speed()
        {
            return Character.COEFFICIENT_SPEED + Level + Power / 4;
        }
    }
}